#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "MotifLook.h"

#include "Env.h"
#include "WindowSystem.h"
#include "Box.h"
#include "Buttons.h"
#include "Layout.h"
#include "Math.h"

//---- Motif -------------------------------------------------------------------

MotifLook *gMotif;

Look *MakeMotifLook()
{
	return new MotifLook;
}

//---- MotifMenuBorderLayout ---------------------------------------------------

class MotifMenuBorderLayout : public Layout {
public:
	MotifMenuBorderLayout()
		{ }
	void SetOrigin(VObject*, const Point&);
	void SetExtent(VObject*, const Point&);
	Metric GetMinSize(VObject *acv);
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

Metric MotifMenuBorderLayout::GetMinSize(VObject *acv)
{
	Point e(acv->At(0)->GetMinSize().extent + gPoint4);
	if (acv->Size() > 1) {
		Metric tm= acv->At(1)->GetMinSize();
		e.x= Math::Max(e.x, tm.extent.x);
		e.y+= tm.extent.y + 1;
	}
	return e;
}

void MotifMenuBorderLayout::SetExtent(VObject *acv, const Point &ee)
{
	Point e(ee);
	if (acv->Size() > 1) {
		acv->At(1)->CalcExtent();
		e.y-= acv->At(1)->Height() + 1;
	}
	acv->At(0)->SetExtent(e-gPoint4);
}

void MotifMenuBorderLayout::SetOrigin(VObject *acv, const Point &a)
{
	Point at(a);
	if (acv->Size() > 1) {
		Point a(at);
		a.x+= (acv->At(0)->Width()-acv->At(1)->Width())/2;
		acv->At(1)->SetOrigin(a);
		at.y+= acv->At(1)->Height() + 1;
	}
	acv->At(0)->SetOrigin(at+gPoint2);
}

void MotifMenuBorderLayout::Adorn(VObject *acv, const Rectangle &r, int)
{
	GrPaintRect(r, gMotif->backgroundColor);
	if (acv->Size() > 1) {
		int titleht= acv->At(1)->Height();
		BevelLine(Point(2,titleht+1), Point(r.extent.x-2,titleht+1), TRUE,
												gMotif->dark, gMotif->light);
		acv->At(1)->DrawAll(r);
	}
	acv->At(0)->DrawAll(r);
	BevelRect(acv->contentRect, 2, TRUE, gMotif->dark, gMotif->light);
}

//---- MotifMenuLineLayout -----------------------------------------------------

class MotifMenuLineLayout : public Layout {
public:
	MotifMenuLineLayout()
		{ }
	Metric GetMinSize(VObject*)
		{ return gPoint2; }
	void Adorn(VObject *acv, const Rectangle&, int)
		{ BevelLine(acv->contentRect.NW(), acv->contentRect.NE(), TRUE,
					gMotif->dark, gMotif->light); }
};

//---- MotifScrollButtonLayout -------------------------------------------------

class MotifScrollButtonLayout : public Layout {
	int width;
public:
	MotifScrollButtonLayout(int w)
		{ width= w; }
	Metric GetMinSize(VObject*)
		{ return Metric(width); }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void MotifScrollButtonLayout::Adorn(VObject*, const Rectangle &r, int code)
{
	FillArrow(r.Inset(2), TESTBIT(code, 4) ? gHighlightColor
										: gMotif->backgroundColor, code & 0x03);
	BevelArrow(r.Inset(2), 2, code & 0x03, !TESTBIT(code, 4), gMotif->dark,
															gMotif->light);
}

//---- MotifToggleButtonLayout -------------------------------------------------

class MotifToggleButtonLayout : public Layout {
	int ht;
	bool toggle;
public:
	MotifToggleButtonLayout(bool t)
		{ toggle= t; ht= gSysFont->Ascender()+gSysFont->Descender(); }
	Metric GetMinSize(VObject *acv);
	void SetOrigin(VObject *acv, const Point &at);
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

Metric MotifToggleButtonLayout::GetMinSize(VObject *acv)
{
	Metric m(gSysFont->GetMetric((byte*)"W"));
	m.extent.x= ht;
	if (acv->Size() > 0)
		m= HCat(m, acv->At(0)->GetMinSize(), 6);
	return m;
}

void MotifToggleButtonLayout::SetOrigin(VObject *acv, const Point &a)
{
	if (acv->Size() > 0) {
		Point at(a);
		at.x+= ht+6;
		acv->At(0)->SetOrigin(at);
	}
}

void MotifToggleButtonLayout::Adorn(VObject *acv, const Rectangle &r, int code)
{
	Rectangle rr(acv->GetOrigin(), Point(ht));

	rr.origin.x+= 2;
	rr.origin.y+= (acv->GetExtent().y-ht)/2;

	if (acv->Size() > 0)
		acv->At(0)->DrawAll(r);

	if (toggle) {
		if (TESTBIT(code, 3))
			GrPaintRect(rr.Inset(3), gHighlightColor);
		else if (!TESTBIT(code, 1))
			GrPaintRect(rr.Inset(3), gMotif->backgroundColor);
		else
			GrPaintRect(rr.Inset(3), gMotif->recessed);
		BevelRect(rr.Inset(1), 2, !(TESTBIT(code, 1) || TESTBIT(code, 3)), gMotif->dark,
																gMotif->light);
	} else {
		if (TESTBIT(code, 3))
			FillDiamond(rr.Inset(2), gHighlightColor);
		else if (TESTBIT(code, 1))
			FillDiamond(rr.Inset(2), gMotif->recessed);
		else if (TESTBIT(code, 3))
			FillDiamond(rr.Inset(2), gHighlightColor);
		BevelDiamond(rr, 2, !(TESTBIT(code, 1) || TESTBIT(code, 3)), gMotif->dark,
																gMotif->light);
	}
}

//---- MotifMenuItemLayout -----------------------------------------------------

class MotifMenuItemLayout : public Layout {
public:
	MotifMenuItemLayout()
		{ }
	Metric GetMinSize(VObject *acv);
	void SetOrigin(VObject *acv, const Point &at);
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

Metric MotifMenuItemLayout::GetMinSize(VObject *acv)
{
	Metric m;
	if (acv->Size() > 0) {
		m= acv->At(0)->GetMinSize();
		if (acv->Size() > 1)
			m= HCat(m, acv->At(1)->GetMinSize());
		m.extent.x+= 2+15+0+5+11;
	}
	return m;
}

void MotifMenuItemLayout::SetOrigin(VObject *acv, const Point &a)
{
	Point at(a);
	if (acv->Size() > 1)
		acv->At(1)->Align(at, acv->GetExtent(), eVObjHRight);
	at.x+= 2+15+0;
	acv->At(0)->SetOrigin(at);
}

void MotifMenuItemLayout::Adorn(VObject *acv, const Rectangle &r, int code)
{
	Rectangle cr(acv->contentRect), rr(cr);
	rr.origin.x+= 2;
	rr.origin.y+= (cr.extent.y-15)/2;
	rr.extent= 15;

	//Ink *ink= TESTBIT(code, 5) ? gMotif->dark : ePatGrey50;

	if (TESTBIT(code, 3)) {
		if (GrHasColor())
			GrPaintRect(cr, gHighlightColor);
	} else
		GrPaintRect(cr, gMotif->backgroundColor);

	acv->Draw(r);

	if (TESTBIT(code, 2)) {
		switch (code & 3) {
		case 1:
			BevelDiamond(rr, 2, TRUE, gMotif->dark, gMotif->light);
			break;
		case 2:
			BevelRect(rr.Inset(1), 2, TRUE, gMotif->dark, gMotif->light);
			break;
		}
	}

	if (TESTBIT(code, 4)) {
		Rectangle rr(Point(11));
		rr.origin= cr.NE()-Point(15,-4);
		BevelArrow(rr, 2, 3, TRUE, gMotif->dark, gMotif->light);
	}

	if (TESTBIT(code, 3)) {
		if (GrHasColor())
			BevelRect(cr, 2, TRUE, gMotif->dark, gMotif->light);
		else
			GrPaintRect(cr, gHighlightColor);
	}
}

//---- MotifScrollBarLayout ----------------------------------------------------

class MotifScrollBarLayout : public Layout {
public:
	MotifScrollBarLayout()
		{ }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void MotifScrollBarLayout::Adorn(VObject *acv, const Rectangle &r, int)
{
	Rectangle contentRect(acv->contentRect);
	if (GrHasColor())
		GrPaintRect(contentRect.Inset(2), gMotif->recessed);
	BevelRect(contentRect, 2, FALSE, gMotif->dark, gMotif->light);
	((Expander*)acv)->Expander::Draw(r);
}

//---- MotifActionButtonLayout -------------------------------------------------

class MotifActionButtonLayout : public Layout {
	bool dflt;
public:
	MotifActionButtonLayout(bool d)
		{ dflt= d; }
	void Adorn(VObject *acv, const Rectangle &r, int code);
	Metric GetMinSize(VObject *acv)
		{ return acv->At(0)->GetMinSize().Expand(dflt ? gPoint6 : gPoint2); }
};

void MotifActionButtonLayout::Adorn(VObject *acv, const Rectangle &r, int code)
{
	Rectangle rr(r);
	VObject *vop= acv->At(0);

	if (dflt)
		rr= rr.Inset(4);

	GrPaintRect(acv->contentRect, gMotif->backgroundColor);

	if (TESTBIT(code, 3)) {
		if (GrHasColor())
			GrPaintRect(rr, gHighlightColor);
	} else if (TESTBIT(code, 1))
		GrPaintRect(rr, gMotif->recessed);

	vop->DrawAll(r);

	if (TESTBIT(code, 3) && !GrHasColor())
		GrPaintRect(rr, gHighlightColor);

	BevelRect(rr, 2, !(TESTBIT(code, 1)||TESTBIT(code, 3)), gMotif->dark, gMotif->light);
	if (dflt)
		BevelRect(r, 1, FALSE, gMotif->dark, gMotif->light);
}

//---- MotifPopupButtonLayout --------------------------------------------------

class MotifPopupButtonLayout : public Layout {
public:
	MotifPopupButtonLayout()
		{ }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void MotifPopupButtonLayout::Adorn(VObject *acv, const Rectangle &, int)
{
	BevelRect(acv->contentRect, 2, TRUE, gMotif->dark, gMotif->light);
	Rectangle r(acv->Width()-18, (acv->Height()-6)/2, 12, 6);
	r.origin+= acv->GetOrigin();
	BevelRect(r, 2, TRUE, gMotif->dark, gMotif->light);
}

//---- MotifThumbLayout --------------------------------------------------------

class MotifThumbLayout : public Layout {
	int width;
public:
	MotifThumbLayout(int w)
		{ width= w; }
	Metric GetMinSize(VObject*)
		{ return Metric(width); }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void MotifThumbLayout::Adorn(VObject*, const Rectangle &r, int)
{
	GrPaintRect(r.Inset(2), gMotif->backgroundColor);
	BevelRect(r.Inset(2), 2, TRUE, gMotif->dark, gMotif->light);
}

//---- MotifFieldBorderLayout --------------------------------------------------

class MotifFieldBorderLayout : public Layout {
	bool noborder;
public:
	MotifFieldBorderLayout(bool nb)
		{ noborder= nb; }
	void Adorn(VObject *acv, const Rectangle &r, int code);
	long GetValue(VObject*, int)
		{ return noborder ? 2 : 4; }
};

void MotifFieldBorderLayout::Adorn(VObject *acv, const Rectangle &, int code)
{
	if (!noborder)
		BevelRect(acv->contentRect.Inset(gPoint2), 2, FALSE, gMotif->dark, gMotif->light);
	if (code)
		GrGetPort()->StrokeRect(gHighlightColor, 2, acv->contentRect);
}

//---- MotifMenuBarLayout ------------------------------------------------------

class MotifMenuBarLayout : public Layout {
	int border;
public:
	MotifMenuBarLayout(int b)
		{ border= b; }
	Metric GetMinSize(VObject*);
	void SetOrigin(VObject *acv, const Point &at)
		{ acv->At(0)->SetOrigin(at+border); }
	void SetExtent(VObject *acv, const Point &e)
		{ acv->At(0)->SetExtent(e-2*border); }
	void Adorn(VObject *acv, const Rectangle &, int)
		{ BevelRect(acv->contentRect, 2, TRUE, gMotif->dark, gMotif->light); }
};

Metric MotifMenuBarLayout::GetMinSize(VObject *acv)
{
	Metric m(acv->At(0)->GetMinSize());
	m.extent+= 2*border;
	m.base+= border;
	return m;
}

//---- MotifSashLayout ---------------------------------------------------------

class MotifSashLayout : public Layout {
	int width;
public:
	MotifSashLayout(int w)
		{ width= w; }
	Metric GetMinSize(VObject*)
		{ return 6; }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void MotifSashLayout::Adorn(VObject *acv, const Rectangle &, int)
{
	Point p1, p2;
	Rectangle rr(acv->contentRect);
	if (rr.extent.x > rr.extent.y) { // horizontal
		p1= rr.W();
		p2= rr.E();
		p1.y--;
		p2.y--;
		rr.origin.x+= rr.extent.x - width - 1;
		rr.extent.x= width;
	} else {    // vertical
		p1= rr.N();
		p2= rr.S();
		p1.x--;
		p2.x--;
		rr.origin.y+= rr.extent.y - width - 1;
		rr.extent.y= width;
	}
	BevelLine(p1, p2, FALSE, gMotif->dark, gMotif->light);
	BevelRect(rr, 2, TRUE, gMotif->dark, gMotif->light);
	GrPaintRect(rr.Inset(2), gMotif->backgroundColor);
}

//---- MotifProgressBarLayout --------------------------------------------------

class MotifProgressBarLayout : public Layout {
	int width;
public:
	MotifProgressBarLayout(int w)
		{ width= w; }
	Metric GetMinSize(VObject*)
		{ return Metric(Math::Min(400, width*10), width); }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void MotifProgressBarLayout::Adorn(VObject *acv, const Rectangle &, int val)
{
	Rectangle rr(acv->contentRect.Inset(2));
	val= Math::Min(val, rr.extent.x);
	GrPaintRect(rr, gMotif->recessed);
	BevelRect(acv->contentRect, 2, FALSE, gMotif->dark, gMotif->light);
	rr.extent.x= val;
	GrPaintRect(rr, gMotif->backgroundColor);
	BevelRect(rr, 2, TRUE, gMotif->dark, gMotif->light);
}

//---- MotifScaleLayout --------------------------------------------------------

class MotifScaleLayout : public Layout {
public:
	MotifScaleLayout()
		{ }
	Metric GetMinSize(VObject *acv)
		{ return acv->At(0)->GetMinSize(); }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void MotifScaleLayout::Adorn(VObject *acv, const Rectangle &, int)
{
	Rectangle cr(acv->contentRect);
	GrPaintRect(cr.Inset(2), gMotif->recessed);
	BevelRect(cr, 2, FALSE, gMotif->dark, gMotif->light);
}

//---- MotifScaleThumbLayout ---------------------------------------------------

class MotifScaleThumbLayout : public Layout {
	Direction dir;
	int width;
public:
	MotifScaleThumbLayout(Direction d, int w)
		{ dir= d; width= w; }
	Metric GetMinSize(VObject *acv);
	void Adorn(VObject *acv, const Rectangle &, int code);
};

Metric MotifScaleThumbLayout::GetMinSize(VObject*)
{
	if (dir == eHor)
		return Metric(width+width/2, width);
	return Metric(width, width+width/2);
}

void MotifScaleThumbLayout::Adorn(VObject *acv, const Rectangle &, int)
{
	Rectangle cr(acv->contentRect.Inset(2));
	GrPaintRect(cr, gMotif->backgroundColor);
	cr.extent[dir]/= 2;
	BevelRect(cr, 2, TRUE, gMotif->dark, gMotif->light);
	cr.origin[dir]+= cr.extent[dir];
	BevelRect(cr, 2, TRUE, gMotif->dark, gMotif->light);
}

//---- MotifLayout --------------------------------------------------------

class MotifGroupLayout : public Layout {
	Point border;
	int cBorderTitleGap;
public:
	MotifGroupLayout(int b)
		{ border= b*2; cBorderTitleGap= b; }
	void SetOrigin(VObject*, const Point&);
	void SetExtent(VObject*, const Point&);
	Metric GetMinSize(VObject *acv);
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

Metric MotifGroupLayout::GetMinSize(VObject *acv)
{
	VObject *interior= acv->At(0);
	Metric m= interior->GetMinSize();

	if (acv->Size() > 1) {
		VObject *titleBar= acv->At(1);
		Metric tm= titleBar->GetMinSize();
		m.extent.x= Math::Max(m.extent.x, tm.extent.x+2*cBorderTitleGap);
		int d= tm.extent.y - border.y;
		if (d > 0) {
			m.extent.y+= d;
			m.base+= d;
		}
	}
	return m.Expand(border);
}

void MotifGroupLayout::SetExtent(VObject *acv, const Point &ee)
{
	Point e(ee);
	if (acv->Size() > 1) {
		VObject *titleBar= acv->At(1);
		titleBar->CalcExtent();
		int d= titleBar->Height() - border.y;
		if (d > 0)
			e.y-= d;
	}
	acv->At(0)->SetExtent(e-2*border);
}

void MotifGroupLayout::SetOrigin(VObject *acv, const Point &a)
{
	Point at(a);
	if (acv->Size() > 1) {
		VObject *titleBar= acv->At(1);
		int th= titleBar->Height();
		int tgap= border.x+cBorderTitleGap;
		titleBar->Align(Point(at.x+tgap, at.y), Metric(acv->Width()-2*tgap, th),
								(VObjAlign)((eVObjHLeft & eVObjH) | eVObjVTop));
		int d= th - border.y;
		if (d > 0)
			at.y+= d;
	}
	acv->At(0)->SetOrigin(at+border);
}

void MotifGroupLayout::Adorn(VObject *acv, const Rectangle &r, int)
{
	if (! acv->At(0)->contentRect.ContainsRect(r)) {
		Rectangle rr(acv->contentRect);
		if (acv->Size() > 1) {
			VObject *titleBar= acv->At(1);
			int h= titleBar->Height()/2;
			rr.origin.y+= h;
			rr.extent.y-= h;

			int lpos= titleBar->GetOrigin().x;
			int rpos= titleBar->contentRect.NE().x;
			rr.extent-= gPoint1;

			GrPaintLine(gMotif->dark, 1, eDefaultCap, Point(lpos, rr.origin.y), rr.NW());
			GrPaintLine(gMotif->dark, 1, eDefaultCap, rr.NW(), rr.SW());
			GrPaintLine(gMotif->dark, 1, eDefaultCap, rr.SW(), rr.SE());
			GrPaintLine(gMotif->dark, 1, eDefaultCap, rr.SE(), rr.NE());
			GrPaintLine(gMotif->dark, 1, eDefaultCap, rr.NE(), Point(rpos, rr.origin.y));

			rr.origin+= gPoint1;
			GrPaintLine(gMotif->light, 1, eDefaultCap, Point(lpos, rr.origin.y), rr.NW());
			GrPaintLine(gMotif->light, 1, eDefaultCap, rr.NW(), rr.SW());
			GrPaintLine(gMotif->light, 1, eDefaultCap, rr.SW(), rr.SE());
			GrPaintLine(gMotif->light, 1, eDefaultCap, rr.SE(), rr.NE());
			GrPaintLine(gMotif->light, 1, eDefaultCap, rr.NE(), Point(rpos, rr.origin.y));
		} else {
			rr.extent-= gPoint1;
			BevelRect(rr, 1, FALSE, gMotif->dark, gMotif->light);
			rr.origin+= gPoint1;
			BevelRect(rr, 1, TRUE, gMotif->dark, gMotif->light);
		}
	}
}

//---- MotifFrameLayout --------------------------------------------------------

class MotifFrameLayout : public Layout {
	int border;
public:
	MotifFrameLayout(int b)
		{ border= b; }
	void Adorn(VObject*, const Rectangle&, int);
	long GetValue(VObject*, int code)
		{ return code ? border+2 : 2; }
};

void MotifFrameLayout::Adorn(VObject*, const Rectangle &r, int code)
{
	BevelRect(r.Expand(gPoint2), 2, FALSE, gMotif->dark, gMotif->light);
	//if (code)
	//       GrGetPort()->StrokeRect(gHighlightColor, 2, r.Expand(gPoint4));
}

//---- Motif -------------------------------------------------------------------

MotifLook::MotifLook()
{
	gMotif= this;

	if (gDepth == 1) {
		light= ePatGrey50;
		dark= gInkBlack;
		recessed= ePatGrey50;
	} else if (gDepth == 2) {           // special case for NeXT
		backgroundColor= new_Grey(0.6);
		light= gInkWhite;
		dark= gInkBlack;
		recessed= new_Grey(0.6);
		disableInk= new_Grey(0.3);
	} else {
		backgroundColor= new_Ink("WindowSystem.WindowBackgroundColor", new_Grey(0.85));
		light= new_Grey(0.95);
		dark= new_Grey(0.15);
		recessed= new_Grey(0.65);
		disableInk= new_Ink("WindowSystem.DisableColor", new_Grey(0.70));
	}

	int width= Env::GetValue("ScrollBar.Width", 20);
	int border= Env::GetValue("Look.Border", 3);

	popUpMenuLayout= new MotifMenuBorderLayout;
	pullDownMenuLayout= new MotifMenuBorderLayout;
	menuLineLayout= new MotifMenuLineLayout;
	menuItemLayout= new MotifMenuItemLayout;
	scrollButtonLayout= new MotifScrollButtonLayout(width);
	radioButtonLayout= new MotifToggleButtonLayout(FALSE);
	toggleButtonLayout= new MotifToggleButtonLayout(TRUE);
	scrollBarLayout= new MotifScrollBarLayout;
	actionButtonLayout= new MotifActionButtonLayout(FALSE);
	defaultButtonLayout= new MotifActionButtonLayout(TRUE);
	popupButtonLayout= new MotifPopupButtonLayout;
	sliderLayout= new Layout;
	thumbLayout= new MotifThumbLayout(width);
	fieldBorderLayout= new MotifFieldBorderLayout(FALSE);
	fieldLayout= new MotifFieldBorderLayout(TRUE);
	menuBarLayout= new MotifMenuBarLayout(border);
	sashLayout= new MotifSashLayout(width);
	progressBarLayout= new MotifProgressBarLayout(width);
	scaleLayout= new MotifScaleLayout;
	scaleThumbLayout[eHor]= new MotifScaleThumbLayout(eHor, width);
	scaleThumbLayout[eVert]= new MotifScaleThumbLayout(eVert, width);
	groupLayout= new MotifGroupLayout(border);
	frameLayout= new MotifFrameLayout(border);
}

MotifLook::~MotifLook()
{
}

void MotifLook::DrawHighlight(Rectangle &r)
{
	//GrPaintRect(r.Inset(2), gHighlightColor);
	//BevelRect(r, 2, TRUE, dark, light);
	GrPaintRect(r, gHighlightColor);
}

//---- Utilities ---------------------------------------------------------------

void BevelArrow(const Rectangle &r, int w, int dir, bool raised, Ink *C1, Ink *C2)
{
	Ink *c1, *c2;
	if (!raised) {
		c1= C1;
		c2= C2;
	} else {
		c1= C2;
		c2= C1;
	}
	switch (dir) {
	case 0: // up
		GrPaintLine(c2, 1, eDefaultCap, r.N(), r.SE());
		GrPaintLine(c2, 1, eDefaultCap, r.SE(), r.SW());
		GrPaintLine(c1, 1, eDefaultCap, r.SW(), r.N());
		break;
	case 1: // down
		GrPaintLine(c1, 1, eDefaultCap, r.NW(), r.NE());
		GrPaintLine(c2, 1, eDefaultCap, r.NE(), r.S());
		GrPaintLine(c1, 1, eDefaultCap, r.S(), r.NW());
		break;
	case 2: // left
		GrPaintLine(c1, 1, eDefaultCap, r.W(), r.NE());
		GrPaintLine(c2, 1, eDefaultCap, r.NE(), r.SE());
		GrPaintLine(c2, 1, eDefaultCap, r.SE(), r.W());
		break;
	case 3: // right
		GrPaintLine(c1, 1, eDefaultCap, r.NW(), r.E());
		GrPaintLine(c2, 1, eDefaultCap, r.E(), r.SW());
		GrPaintLine(c1, 1, eDefaultCap, r.SW(), r.NW());
		break;
	}

	if (w >= 2)
		BevelArrow(r.Inset(1), w-1, dir, raised, C1, C2);
}

void BevelRect(const Rectangle &r, int w, bool raised, Ink *C1, Ink *C2)
{
	Ink *c1, *c2;
	if (!raised) {
		c1= C1;
		c2= C2;
	} else {
		c1= C2;
		c2= C1;
	}
	GrPaintLine(c1, 1, eDefaultCap, r.SW(), r.NW());
	GrPaintLine(c1, 1, eDefaultCap, r.NW(), r.NE());
	GrPaintLine(c2, 1, eDefaultCap, r.SW(), r.SE());
	GrPaintLine(c2, 1, eDefaultCap, r.SE(), r.NE());
	if (w >= 2)
		BevelRect(r.Inset(1), w-1, raised, C1, C2);
}

void BevelDiamond(const Rectangle &rr, int w, bool raised, Ink *C1, Ink *C2)
{
	Rectangle r(rr);
	if ((r.extent.x & 1) == 0)
		r.extent.x--;
	if ((r.extent.y & 1) == 0)
		r.extent.y--;
	Ink *c1, *c2;
	if (!raised) {
		c1= C1;
		c2= C2;
	} else {
		c1= C2;
		c2= C1;
	}
	GrPaintLine(c2, 1, eDefaultCap, r.W(), r.S());
	GrPaintLine(c2, 1, eDefaultCap, r.S(), r.E());
	GrPaintLine(c1, 1, eDefaultCap, r.W(), r.N());
	GrPaintLine(c1, 1, eDefaultCap, r.N(), r.E());
	if (w >= 2)
		BevelDiamond(rr.Inset(1), w-1, raised, C1, C2);
}

void BevelLine(const Point &p1, const Point &p2, bool raised, Ink *C1, Ink *C2)
{
	Ink *c1, *c2;
	if (!raised) {
		c1= C1;
		c2= C2;
	} else {
		c1= C2;
		c2= C1;
	}
	GrPaintLine(c1, 1, eDefaultCap, p1, p2);
	GrPaintLine(c2, 1, eDefaultCap, p1+gPoint1, p2+gPoint1);
}

void FillDiamond(const Rectangle &rr, Ink *fill)
{
	Point p[4];
	Rectangle r(rr);
	if ((r.extent.x & 1) == 0)
		r.extent.x--;
	if ((r.extent.y & 1) == 0)
		r.extent.y--;
	p[0]= r.N();
	p[1]= r.E();
	p[2]= r.S();
	p[3]= r.W();
	GrPaintPolygon(gPoint0, fill, p, 4, ePolyDefault);
	GrPaintLine(fill, 1, eDefaultCap, r.N(), r.E());
	GrPaintLine(fill, 1, eDefaultCap, r.E(), r.S());
}

void FillArrow(const Rectangle &rr, Ink *fill, int dir)
{
	Point p[3];
	Rectangle r(rr);

	switch (dir) {
	case 0: // up
		p[0]= r.N();
		p[1]= r.SE();
		p[2]= r.SW();
		break;
	case 1: // down
		p[0]= r.NW();
		p[1]= r.NE();
		p[2]= r.S();
		break;
	case 2: // left
		p[0]= r.W();
		p[1]= r.NE();
		p[2]= r.SE();
		break;
	case 3: // right
		p[0]= r.NW();
		p[1]= r.E();
		p[2]= r.SW();
		break;
	}

	GrPaintPolygon(gPoint0, fill, p, 3, ePolyDefault);
	GrPaintLine(fill, 1, eDefaultCap, p[0], p[1]);
	GrPaintLine(fill, 1, eDefaultCap, p[1], p[2]);
	GrPaintLine(fill, 1, eDefaultCap, p[2], p[0]);
}
